﻿using Model.Flow;
using System.Linq.Dynamic.Core;
using Utility;
using Utility.Enum;

namespace CoreDomain
{
    /// <summary>
    /// 流程核心
    /// </summary>
    public static class FlowCore
    {
        /// <summary>
        /// 验证流程是否设置正确
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <returns></returns>
        public static Tuple<string, bool> VerifyFlow(FlowModel flowModel)
        {
            //验证节点和节点关系
            if (flowModel.nodes.Count == 0)
                return new Tuple<string, bool>("流程未添加任何节点!", false);
            if (flowModel.edges.Count == 0)
                return new Tuple<string, bool>("流程节点未确定流转关系!", false);

            //是否存在开始节点
            var isHaveStartNode = flowModel.nodes.Any(f => f.type == FlowNodeTypeEnum.input.ToString());
            if (!isHaveStartNode)
                return new Tuple<string, bool>("流程必须存在开始节点!", false);

            //是否存在归档节点
            var isHaveEndNode = flowModel.nodes.Any(f => f.type == FlowNodeTypeEnum.output.ToString());
            if (!isHaveEndNode)
                return new Tuple<string, bool>("流程必须存在归档节点!", false);

            //是否存在普通节点
            var isHaveOrdinaryNode = flowModel.nodes.Any(f => (f.type == FlowNodeTypeEnum.ordinaryNode.ToString() || f.type == FlowNodeTypeEnum.countersign.ToString()));
            if (!isHaveOrdinaryNode)
                return new Tuple<string, bool>("流程必须包含至少一个普通节点或会签节点!", false);

            //节点验证
            foreach (var item in flowModel.nodes)
            {
                //验证节点是否设置审核信息
                if (VerifyNodel(item))
                {
                    return new Tuple<string, bool>(string.Format("节点【{0}】未设置审核信息!", item.data.label), false);
                }
                //验证节点是否存在关系
                if (VerifyNodel(item) || item.type == FlowNodeTypeEnum.input.ToString())
                {
                    if (!flowModel.edges.Any(f => f.source == item.id))
                        return new Tuple<string, bool>(string.Format("节点【{0}】未确定流转关系!", item.data.label), false);
                }
                if (VerifyNodel(item) || item.type == FlowNodeTypeEnum.output.ToString())
                {
                    if (!flowModel.edges.Any(f => f.target == item.id))
                        return new Tuple<string, bool>(string.Format("节点【{0}】未确定流转关系!", item.data.label), false);
                }

                if (item.type != FlowNodeTypeEnum.output.ToString() && item.type != FlowNodeTypeEnum.input.ToString() && item.type != FlowNodeTypeEnum.czLane.ToString() && item.type != FlowNodeTypeEnum.spLane.ToString())
                {
                    if (!flowModel.edges.Any(f => f.target == item.id))
                        return new Tuple<string, bool>(string.Format("节点【{0}】未确定流转关系!", item.data.label), false);
                    if (!flowModel.edges.Any(f => f.source == item.id))
                        return new Tuple<string, bool>(string.Format("节点【{0}】未确定流转关系!", item.data.label), false);
                }
            }

            return new Tuple<string, bool>("流程设置成功", true);
        }

        /// <summary>
        /// 获取开始节点
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <returns>开始节点</returns>
        public static NodeModel GetStartNode(FlowModel flowModel)
        {
            var startNode = flowModel.nodes.Where(f => f.type == FlowNodeTypeEnum.input.ToString()).FirstOrDefault();
            return startNode;
        }

        /// <summary>
        /// 开始流程,并返回下一个节点信息
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="formData">表单中的字段数据</param>
        /// <returns>下一个节点信息</returns>
        public static NodeModel StartFlow(FlowModel flowModel, string formData = null)
        {
            var startNodeId = flowModel.nodes.Where(f => f.type == FlowNodeTypeEnum.input.ToString()).FirstOrDefault()?.id ?? string.Empty;
            if (string.IsNullOrWhiteSpace(startNodeId))
                ExceptionHelper.ThrowBusinessException("流程不存在开始节点!");
            return GetNextFlowNodeMsg(flowModel, startNodeId, formData);
        }

        /// <summary>
        /// 获取下一个流程节点信息
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="currentNodeId">当前节点id</param>
        /// <param name="formData">表单中的字段数据</param>
        /// <returns></returns>
        public static NodeModel GetNextFlowNodeMsg(FlowModel flowModel, string currentNodeId, string formData = null)
        {
            NodeModel nodeModel = new();
            var currentNodelData = flowModel.nodes.Where(f => f.id == currentNodeId).FirstOrDefault();
            if (currentNodelData == null)
                ExceptionHelper.ThrowBusinessException("未找到当前节点信息!");
            if (currentNodelData.type == FlowNodeTypeEnum.output.ToString())
                ExceptionHelper.ThrowBusinessException("当前节点为归档节点,流程已结束!");

            var nextNodeEdgeList = flowModel.edges.Where(f => f.source == currentNodeId).ToList();
            if (nextNodeEdgeList.Count == 0)
                ExceptionHelper.ThrowBusinessException("当前节点不存在节点关系!");

            foreach (var item in nextNodeEdgeList)
            {
                //如果为经过条件为空,
                if (string.IsNullOrEmpty(item.data.edgeId))
                {
                    nodeModel = flowModel.nodes.Where(f => f.id == item.target).FirstOrDefault() ?? new NodeModel();
                    break;
                }
                else
                {
                    var edgesExtendModel = item.data;
                    edgesExtendModel.edgeWhere = GetEdgeWhereValue(formData, item.data);
                    var lambdaExpression = string.Format(" edgeWhere {0} \"{1}\" ", item.data.edgeWhereEquation, item.data.edgeWhereValue);
                    if (MatchLambdaExpression(lambdaExpression, item.data))
                    {
                        nodeModel = flowModel.nodes.Where(f => f.id == item.target).FirstOrDefault() ?? new NodeModel();
                        edgesExtendModel.edgeWhere = string.Empty;
                        break;
                    }
                    edgesExtendModel.edgeWhere = string.Empty;
                }
            }
            if (string.IsNullOrWhiteSpace(nodeModel.id))
                ExceptionHelper.ThrowBusinessException("当前节点不存在下一个节点!");
            return nodeModel;
        }

        /// <summary>
        /// 获取上一个流程节点信息
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="currentNodeId">当前节点id</param>
        /// <param name="formData">表单中的字段数据</param>
        /// <returns></returns>
        public static NodeModel GetPreviousFlowNodeMsg(FlowModel flowModel, string currentNodeId, string formData = null)
        {
            NodeModel nodeModel = new();
            var currentNodelData = flowModel.nodes.Where(f => f.id == currentNodeId).FirstOrDefault();
            if (currentNodelData == null)
                ExceptionHelper.ThrowBusinessException("未找到当前节点信息!");

            var previousNodeEdgeList = flowModel.edges.Where(f => f.target == currentNodeId).ToList();
            if (previousNodeEdgeList.Count == 0)
                ExceptionHelper.ThrowBusinessException("当前节点不存在节点关系!");

            foreach (var item in previousNodeEdgeList)
            {
                //如果为经过条件为空,
                if (string.IsNullOrEmpty(item.data.edgeId))
                {
                    nodeModel = flowModel.nodes.Where(f => f.id == item.source).FirstOrDefault() ?? new NodeModel();
                    break;
                }
                else
                {
                    var edgesExtendModel = item.data;
                    edgesExtendModel.edgeWhere = GetEdgeWhereValue(formData, item.data);
                    var lambdaExpression = string.Format(" edgeWhere {0} \"{1}\" ", item.data.edgeWhereEquation, item.data.edgeWhereValue);
                    if (MatchLambdaExpression(lambdaExpression, item.data))
                    {
                        nodeModel = flowModel.nodes.Where(f => f.id == item.source).FirstOrDefault() ?? new NodeModel();
                        edgesExtendModel.edgeWhere = string.Empty;
                        break;
                    }
                    edgesExtendModel.edgeWhere = string.Empty;
                }
            }
            if (string.IsNullOrWhiteSpace(nodeModel.id))
                ExceptionHelper.ThrowBusinessException("当前节点不存在上一个节点!");
            return nodeModel;
        }

        /// <summary>
        /// 递归找两节点间的节点数据
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="startNodeId">开始节点</param>
        /// <param name="endNodeId">结束节点</param>
        /// <param name="nodeList">中间节点集合</param>
        /// <param name="formData">表单中的字段数据</param>
        /// <returns></returns>
        public static List<NodeModel> GetBetweenNodeMsg(FlowModel flowModel, string startNodeId, string endNodeId, List<NodeModel> nodeList, string formData = null)
        {
            NodeModel nodeModel = new();
            var startNodeData = flowModel.nodes.Where(f => f.id == startNodeId).FirstOrDefault();
            if (startNodeData == null)
                ExceptionHelper.ThrowBusinessException("【startNodeId】节点不存在!");
            //if (startNodeData.type == FlowNodeTypeEnum.output.ToString())
            //    ExceptionHelper.ThrowBusinessException("【startNodeId】为归档节点,流程已结束!");

            var endNodeData = flowModel.nodes.Where(f => f.id == endNodeId).FirstOrDefault();
            if (endNodeData == null)
                ExceptionHelper.ThrowBusinessException("【endNodeId】节点不存在!");
            //if (endNodeData.type == FlowNodeTypeEnum.output.ToString())
            //    ExceptionHelper.ThrowBusinessException("【endNodeId】为归档节点,流程已结束!");

            var nextNodeEdgeList = flowModel.edges.Where(f => f.source == startNodeId).ToList();
            if (nextNodeEdgeList.Count == 0)
                ExceptionHelper.ThrowBusinessException("当前节点不存在节点关系!");

            foreach (var item in nextNodeEdgeList)
            {
                //如果为经过条件为空,
                if (string.IsNullOrEmpty(item.data.edgeId))
                {
                    nodeModel = flowModel.nodes.Where(f => f.id == item.target).FirstOrDefault() ?? new NodeModel();
                    if (nodeModel.id == endNodeId)
                        break;
                    else
                    {
                        nodeList.Add(nodeModel);
                        GetBetweenNodeMsg(flowModel, nodeModel.id, endNodeId, nodeList, formData);
                    }
                }
                else
                {
                    var edgesExtendModel = item.data;
                    edgesExtendModel.edgeWhere = GetEdgeWhereValue(formData, item.data);
                    var lambdaExpression = string.Format(" edgeWhere {0} \"{1}\" ", item.data.edgeWhereEquation, item.data.edgeWhereValue);
                    if (MatchLambdaExpression(lambdaExpression, item.data))
                    {
                        nodeModel = flowModel.nodes.Where(f => f.id == item.target).FirstOrDefault() ?? new NodeModel();
                        edgesExtendModel.edgeWhere = string.Empty;
                        if (nodeModel.id == endNodeId)
                            break;
                        else
                        {
                            nodeList.Add(nodeModel);
                            GetBetweenNodeMsg(flowModel, nodeModel.id, endNodeId, nodeList, formData);
                        }

                    }
                    edgesExtendModel.edgeWhere = string.Empty;
                }
            }
            if (string.IsNullOrWhiteSpace(nodeModel.id))
                ExceptionHelper.ThrowBusinessException("当前节点不存在下一个节点!");
            return nodeList;
        }

        /// <summary>
        /// 验证当前节点是否是归档节点
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="currentNodeId">当前节点</param>
        public static bool VerifyStartNode(FlowModel flowModel, string currentNodeId)
        {
            var isTrue = flowModel.nodes.Any(f => f.id == currentNodeId && f.type == FlowNodeTypeEnum.input.ToString());
            return isTrue;
        }

        /// <summary>
        /// 验证当前节点是否是归档节点
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="currentNodeId">当前节点</param>
        public static bool VerifyEndNode(FlowModel flowModel, string currentNodeId)
        {
            var isTrue = flowModel.nodes.Any(f => f.id == currentNodeId && f.type == FlowNodeTypeEnum.output.ToString());
            return isTrue;
        }

        /// <summary>
        /// 匹配lambda表达式
        /// </summary>
        /// <param name="lambdaExpression">匹配表达式</param>
        /// <param name="model">实体</param>
        /// <returns></returns>
        private static bool MatchLambdaExpression(string lambdaExpression, EdgesExtendModel model)
        {
            var lambda = DynamicExpressionParser.ParseLambda<EdgesExtendModel, bool>(ParsingConfig.Default, true, lambdaExpression).Compile(true);
            if (new List<EdgesExtendModel> { model }.Count(lambda) > 0)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 获取连线条件值
        /// </summary>
        /// <param name="formData">表单字段数据</param>
        /// <param name="edgesExtendModel"></param>
        /// <returns></returns>
        private static string GetEdgeWhereValue(string formData, EdgesExtendModel edgesExtendModel)
        {
            if (string.IsNullOrEmpty(formData))
                return string.Empty;
            var dictionary = JsonHelper.DeserializeStringToDictionary<string, object>(formData);
            string whereValue = string.Empty;
            foreach (var item in dictionary)
            {
                //如果选择的条件名等于菜单中的字段
                if (item.Key == edgesExtendModel.edgeWhereName)
                {
                    if (item.Value is string || item.Value is int || item.Value is bool || item.Value is decimal || item.Value is double)
                    {
                        whereValue = item.Value.ToString();
                        break;
                    }
                    else
                    {
                        //数组
                        if (item.Value != null)
                        {

                            var stringJson = item.Value.ToString().Replace("\n", string.Empty).Replace("\r", string.Empty).Replace("\t", string.Empty).Replace(" ", string.Empty);
                            var list = JsonHelper.JsonStrToEntity<List<string>>(stringJson);
                            if (list.Any(f => f == edgesExtendModel.edgeWhereValue))
                            {
                                whereValue = edgesExtendModel.edgeWhereValue;
                                break;
                            }
                        }
                    }
                }
            }
            return whereValue;
        }

        /// <summary>
        /// 设置节点样式
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="nodeId">节点id</param>
        /// <param name="nodeClassStyle">样式枚举</param>
        /// <returns></returns>
        public static FlowModel SetCurrentNodeClass(FlowModel flowModel, string nodeId, NodeClassStyleEnum nodeClassStyle)
        {
            foreach (var item in flowModel.nodes)
            {
                if (item.id == nodeId)
                    item.@class = $"{item.@class.Split([' '], 2)[0]} {nodeClassStyle}";
                if (item.type == FlowNodeTypeEnum.input.ToString())
                    item.@class = $"{item.@class.Split([' '], 2)[0]} {NodeClassStyleEnum.agreeBackColor}";
            }
            return flowModel;
        }

        /// <summary>
        /// 批量设置节点样式
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="nodeId">节点id</param>
        /// <param name="nodeClassStyle">样式枚举</param>
        /// <returns></returns>
        public static FlowModel BatchSetNodeClass(FlowModel flowModel, List<string> nodeIds, NodeClassStyleEnum nodeClassStyle)
        {
            foreach (var item in flowModel.nodes)
            {
                if (nodeIds.Any(f => f == item.id))
                    item.@class = $"{item.@class.Split([' '], 2)[0]} {nodeClassStyle}";
            }
            return flowModel;
        }

        /// <summary>
        /// 设置线条是否流动
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="startNodeId">开始节点</param>
        /// <param name="endNodeId">结束节点</param>
        /// <param name="animated">是否流动</param>
        /// <returns></returns>
        public static FlowModel SetEdgesAnimated(FlowModel flowModel, string startNodeId, string endNodeId, bool animated, EdgesClassStyleEnum edgesClassStyleEnum)
        {
            foreach (var item in flowModel.edges)
            {
                if (item.source == startNodeId && item.target == endNodeId)
                {
                    item.animated = animated;
                    item.style = new ClassStyle { stroke = EnumHelper.GetDescription(edgesClassStyleEnum) };
                }
            }
            return flowModel;
        }

        /// <summary>
        /// 获取当前节点数据
        /// </summary>
        /// <param name="flowModel">流程数据</param>
        /// <param name="nodeId">当前节点id</param>
        /// <returns></returns>
        public static NodeModel GetCurrentNodeData(FlowModel flowModel, string nodeId)
        {
            NodeModel nodeModel = new();
            foreach (var item in flowModel.nodes)
            {
                if (item.id == nodeId)
                {
                    nodeModel = item;
                    break;
                }
            }
            return nodeModel;
        }

        /// <summary>
        /// 验证节点是否审核节点
        /// </summary>
        /// <param name="nodeModel"></param>
        /// <returns></returns>
        private static bool VerifyNodel(NodeModel nodeModel)
        {
            if (nodeModel.type == FlowNodeTypeEnum.ordinaryNode.ToString() && nodeModel.data.reviewerIds.Count == 0)
                return true;
            if (nodeModel.type == FlowNodeTypeEnum.countersign.ToString() && nodeModel.data.reviewerIds.Count == 0)
                return true;
            return false;
        }
    }
}
